/* SPDX-License-Identifier: LGPL-2.1-or-later */

#include <stdio.h>
#include <unistd.h>

#include "env-util.h"
#include "fd-util.h"
#include "fuzz.h"
#include "selinux-util.h"
#include "static-destruct.h"
#include "stdio-util.h"
#include "strv.h"
#include "systemctl.h"
#include "systemctl-util.h"

int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
        _cleanup_strv_free_ char **argv = NULL;
        _cleanup_close_ int orig_stdout_fd = -1;
        int r;

        /* We don't want to fill the logs with messages about parse errors.
         * Disable most logging if not running standalone */
        if (!getenv("SYSTEMD_LOG_LEVEL"))
                log_set_max_level(LOG_CRIT);

        arg_pager_flags = PAGER_DISABLE; /* We shouldn't execute the pager */

        argv = strv_parse_nulstr((const char *)data, size);
        if (!argv)
                return log_oom();

        if (!argv[0])
                return 0; /* argv[0] should always be present, but may be zero-length. */

        if (getenv_bool("SYSTEMD_FUZZ_OUTPUT") <= 0) {
                orig_stdout_fd = fcntl(fileno(stdout), F_DUPFD_CLOEXEC, 3);
                if (orig_stdout_fd < 0)
                        log_warning_errno(orig_stdout_fd, "Failed to duplicate fd 1: %m");
                else
                        assert_se(freopen("/dev/null", "w", stdout));

                opterr = 0; /* do not print errors */
        }

        optind = 0; /* this tells the getopt machinery to reinitialize */

        r = systemctl_dispatch_parse_argv(strv_length(argv), argv);
        if (r < 0)
                log_error_errno(r, "Failed to parse args: %m");
        else
                log_info(r == 0 ? "Done!" : "Action!");

        if (orig_stdout_fd >= 0) {
                char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];

                xsprintf(path, "/proc/self/fd/%d", orig_stdout_fd);
                assert_se(freopen(path, "w", stdout));
        }

        release_busses(); /* We open the bus for communication with logind.
                           * It needs to be closed to avoid apparent leaks. */

        mac_selinux_finish();

        /* Call static destructors to do global state cleanup. We do it here, and not in fuzz-main.c so that
         * any global state is destroyed between fuzzer runs. */
        static_destruct();

        return 0;
}
